import java.io.*;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Scanner;

/**
 * Created by wxc on 2017/2/21.
 *
 * 把DDL语句快速生成Bean 、Example 、Mapper
 *
 * @author wxc
 * @version domwiki 4.0.0
 * @since domwiki 4.0.0
 */
public class DDL2BeanCreater {
    /*
    CREATE TABLE IF NOT EXISTS `shigu_mall`.`item_order_logistics` (
  `id` BIGINT(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `company_id` BIGINT(11) NOT NULL COMMENT '物流公司ID',
  `courier_number` VARCHAR(45) NOT NULL COMMENT '快递单号',
  `money` BIGINT(11) NOT NULL COMMENT '费用',
  `prov_id` BIGINT(11) NOT NULL,
  `city_id` BIGINT(11) NOT NULL,
  `town_id` BIGINT(11) NULL,
  `address` VARCHAR(400) NOT NULL COMMENT '详细地址',
  `telephone` VARCHAR(20) NOT NULL COMMENT '手机号',
  `zip_code` VARCHAR(10) NULL COMMENT '邮编',
  `oid` BIGINT(11) NOT NULL COMMENT '订单ID',
  PRIMARY KEY (`id`),
  INDEX `fk_item_order_logistics_item_order1_idx` (`oid` ASC),
  CONSTRAINT `fk_item_order_logistics_item_order1`
    FOREIGN KEY (`oid`)
    REFERENCES `shigu_mall`.`item_order` (`oid`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB
COMMENT = '物流'
     */

    private static String beanPath;

    private static final String SP = File.separator;

    public static void main(String[] args) throws IOException {
        DDL2BeanCreater ddl2Bean = new DDL2BeanCreater();
        Scanner scanner = new Scanner(System.in);
        if (args.length==1) {
            beanPath=System.getProperty("user.dir")+args[0];
        }else{
            System.out.println("请输入bean 目录(磁盘完整路径, 结尾/):");
            beanPath = scanner.nextLine();
            beanPath = beanPath.replace("\\", SP);
            if (!beanPath.endsWith(SP)) {
                beanPath += SP;
            }
        }
            System.out.println("请粘贴表创建语句(英文分号结束输入)：\n");
            StringBuilder sb = new StringBuilder();
            String tmp;
            while (!(tmp = scanner.nextLine()).endsWith(";")) {
                sb.append(tmp);
            }
            ddl2Bean.getBean(sb.toString());
    }

    public void getBean(String ddl) throws IOException {
        Map<String, String[]> fields = new LinkedHashMap<>();
        Map<String, String> dataFields = new HashMap<>();
        String beanName = "unknown";
        boolean typeHasDate = false;
        for (String s : ddl.split(",")) {
            if (s.contains("PRIMARY KEY")) {
                break;
            }
            if (s.contains("CREATE TABLE")) {
                if (s.contains(".")){
                    s = s.substring(s.indexOf(".") + 1);
                }
                int beanStart = s.indexOf("`");
                int beanEnd = s.indexOf("`", beanStart + 1);
                beanName = dataToField(s.substring(beanStart + 1, beanEnd), true);
                s = s.substring(beanEnd + 1);
            }
            int dataFieldStart = s.indexOf("`");
            if (dataFieldStart == -1) {
                continue;
            }
            int dataFieldEnd = s.indexOf("`", dataFieldStart + 1);
            String dataField = s.substring(dataFieldStart + 1, dataFieldEnd);

            int commentStart = s.indexOf("'", s.indexOf("COMMENT"));
            int commentEnd = s.indexOf("'", commentStart + 1);
            String comment = "";
            if (commentStart > 0 && commentEnd > commentStart)
                comment = s.substring(commentStart + 1, commentEnd);
            String type = "String";
            if (s.contains("bigint") || s.contains("BIGINT"))
                type = "Long";
            else if (s.contains("int") || s.contains("INT"))
                type = "Integer";
            else if (s.contains("varchar") || s.contains("VARCHAR"))
                type = "String";
            else if (s.contains("timestamp") || s.contains("date") || s.contains("TIMESTAMP") || s.contains("DATE")) {
                type = "Date";
                typeHasDate = true;
            } else if (s.contains("bit") || s.contains("BIT"))
                type = "Boolean";
            fields.put(dataToField(dataField, false), new String[]{comment, type});
            dataFields.put(dataField, type);
        }

        int i=0;
        String path = beanPath + beanName + ".java";
        String flag = "src" + SP + "main" + SP + "java";
        PrintWriter out = new PrintWriter(path);
        String beanPack = beanPath.substring(path.lastIndexOf(flag), beanPath.length() - 1).replace(flag + SP, "").replace(SP, ".");
        out.println(String.format("package %s;\n\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\n" + (typeHasDate ? "import java.util.Date;\n" : "") + "import java.io.Serializable;\n\npublic class %s implements Serializable {", beanPack, beanName));
        for (Map.Entry<String, String[]> entry : fields.entrySet()) {
            String[] value = entry.getValue();
            out.println("    /**\n" +
                    "     * " + value[0] +
                    "\n     */");
            if (i++==0)
                out.println("    @Id\n" +
                        "    @GeneratedValue(generator = \"JDBC\")");
            out.println("\tprivate " + value[1] + " " + entry.getKey() + ";\n");
        }

        for (Map.Entry<String, String[]> entry : fields.entrySet()) {
            String[] value = entry.getValue();
            String key = entry.getKey();
            out.println(String.format("\tpublic void set%s(%s %s) {", dataToField(key, true), value[1], key));
            out.println(String.format("\t\tthis.%s = %s;\n\t}\n", key, key));
            out.println(String.format("\tpublic %s get%s() {", value[1], dataToField(key, true)));
            out.println(String.format("\t\treturn this.%s;\n\t}\n", key));
        }
        out.print('}');
        out.flush();
        out.close();
        System.out.println("Bean类生成成功！！");
        createExample(path, dataFields);
    }

    private static String dataToField(String dataField, boolean upper) {
        int i = dataField.indexOf("_");
        if (upper) {
            char[] chars = dataField.toCharArray();
            chars[0] -= 32;
            dataField = String.valueOf(chars);
        }
        if (i != -1) {
            String head = dataField.substring(0, i);
            dataField = head + dataField.substring(i + 1, i + 2).toUpperCase() + dataField.substring(i + 2);
        } else return dataField;
        return dataToField(dataField, false);
    }

    public void createExample(String path, Map<String, String> fields){
        String sprit = SP;
        String dir = path.substring(0, path.lastIndexOf(sprit));
        String parentdir = dir.substring(0, dir.lastIndexOf(sprit));
        String fileName = path.replace(dir + sprit, "");
        String packageName, interfacePackage, beanClassName;
        String flag = "src" + sprit + "main" + sprit + "java";
        if(path.contains(flag)) {
            packageName = path.substring(path.indexOf(flag)).replace(flag + sprit, "").replace(sprit + fileName, "");
            beanClassName = packageName.replace(sprit, ".") + "." + fileName.replace(".java", "");
            packageName = packageName.substring(0, packageName.lastIndexOf(sprit)) + sprit;
            interfacePackage = packageName + "interfaces";
            interfacePackage = interfacePackage.replace(sprit, ".");
            packageName = packageName + "examples";
            packageName = packageName.replace(sprit, ".");
            String beanName = fileName.replace(".java", "");

            try {
                String code = exampleCreate(packageName, fields, beanName);
                File examdir = new File(parentdir + sprit + "examples");
                examdir.mkdir();
                FileOutputStream fos = new FileOutputStream(parentdir + sprit + "examples" + sprit + beanName + "Example.java");
                BufferedOutputStream bos = new BufferedOutputStream(fos);
                OutputStreamWriter osw = new OutputStreamWriter(bos);
                BufferedWriter fw = new BufferedWriter(osw);
                fw.write(code);
                fw.close();
                osw.close();
                bos.close();
                fos.close();
                System.out.println("Example类生成成功！！");
                Scanner scanner = new Scanner(System.in);
                System.out.println("是否继续生成接口类？(y/n):");
                String inter = scanner.nextLine();
                if ("Y".equals(inter.toUpperCase())) {
                    String interfaceCode = interfaceString(beanName, interfacePackage, beanClassName);
                    examdir = new File(parentdir + sprit + "interfaces");
                    examdir.mkdir();
                    fos = new FileOutputStream(parentdir + sprit + "interfaces" + sprit + beanName + "Mapper.java");
                    bos = new BufferedOutputStream(fos);
                    osw = new OutputStreamWriter(bos);
                    fw = new BufferedWriter(osw);
                    fw.write(interfaceCode);
                    fw.close();
                    osw.close();
                    bos.close();
                    fos.close();
                    System.out.println("接口类生成成功！！");
                }
            } catch (Exception var19) {
                var19.printStackTrace();
            }
        } else {
            System.out.println("没有找到src/main/java目录");
        }

    }

    public static String interfaceString(String beanName, String intpackageName, String beanPackage) {
        String repositoryName = beanPackage.replace("com.opentae.data.", "").replace(".beans." + beanName, "");
        return "package " + intpackageName + ";\n" + "\n" + "import com.opentae.core.mybatis.config.MyBatisRepository;\n" + "import com.opentae.core.mybatis.mapper.Mapper;\n" + "import " + beanPackage + ";\n" + "import org.springframework.context.annotation.Lazy;\n" + "import org.springframework.context.annotation.Scope;\n" + "\n" + "/**\n" + " * \n" + " * Created by exampleCreater.\n" + " */\n" + "@MyBatisRepository(\"tae_" + repositoryName + "_" + lowfirst(beanName) + "Mapper\")\n" + "@Scope(\"singleton\")\n" + "@Lazy(true)\n" + "public interface " + beanName + "Mapper extends Mapper<" + beanName + "> {\n" + "}\n";
    }

    private static String mkPackage(String packageStr, String beanClass) {
        return "package " + packageStr + ";\n\nimport com.opentae.core.mybatis.SgExample;\nimport com.opentae.core.mybatis.example.EntityColumn;\nimport com.opentae.core.mybatis.example.EntityTable;\nimport com.opentae.core.mybatis.mapperhelper.EntityHelper;\nimport " + beanClass + ";\n\nimport java.util.*;\n";
    }

    private static String mkOther(String beanName, Map<String, String> fields) {
        String sb = "public class " +
                beanName +
                "Example extends SgExample<" +
                beanName +
                "Example.Criteria> {\n" +
                "    public static final Class<" +
                beanName +
                "> beanClass = " +
                beanName +
                ".class;\n" +
                "    public static final EntityTable entityTable = EntityHelper.getEntityTable(beanClass);\n";
        for (String field : fields.keySet()) {
            sb += "    public static EntityColumn "+field+";\n";
        }
        sb += "    static {\n" +
                "        Set<EntityColumn> columns = entityTable.getEntityClassColumns();\n" +
                "        Map<String, EntityColumn> listMap = new HashMap<>();\n" +
                "        for (EntityColumn column : columns) {\n" +
                "            listMap.put(column.getProperty(), column);\n" +
                "        }\n";
        for (String name : fields.keySet()) {
            sb += "        "+name+" = listMap.get(\""+name+"\");\n";
        }
        sb += "        }\n\n" +
                "    public "+beanName+"Example() {\n" +
                "        this.setTableAlias(entityTable.getName());\n" +
                "    }\n\n";
        sb += "    @Override\n" +
                "    public EntityTable getEntityTable() {\n" +
                "        return entityTable;\n" +
                "    }\n\n";
        sb += "    @Override\n    protected " +
                beanName +
                "Example.Criteria createCriteriaInternal() {\n        return new " +
                beanName +
                "Example.Criteria(this);\n    }\n\n" +
                "    public static class Criteria extends SgExample.GeneratedCriteria<Criteria> {\n" +
                "        protected Criteria(SgExample example) {\n" +
                "            super(example);\n" +
                "        }\n\n";
        return sb;
    }

    private static String createFields(Map<String, String> fields, String beanName) throws ClassNotFoundException {
        StringBuilder sb = new StringBuilder();
        final String methodHead = "        public " + beanName + "Example.Criteria and";
        for (Map.Entry<String, String> f : fields.entrySet()) {
            String fname = f.getKey();
            String ftype = f.getValue();
            String head = methodHead + upfirst(fname);
            sb.append(head);
            sb.append("IsNull() {\n            return isNull(");
            sb.append(fname);
            sb.append(");\n        }\n\n");

            sb.append(head);
            sb.append("IsNotNull() {\n            return isNotNull(");
            sb.append(fname);
            sb.append(");\n        }\n\n");

            sb.append(head);
            sb.append("EqualTo(");
            sb.append(ftype);
            sb.append(" value) {\n            return equalTo(");
            sb.append(fname);
            sb.append(", value);\n        }\n\n");

            sb.append(head);
            sb.append("NotEqualTo(");
            sb.append(ftype);
            sb.append(" value) {\n            return notEqualTo(");
            sb.append(fname);
            sb.append(", value);\n        }\n\n");

            sb.append(head);
            sb.append("GreaterThan(");
            sb.append(ftype);
            sb.append(" value) {\n            return greaterThan(");
            sb.append(fname);
            sb.append(", value);\n        }\n\n");

            sb.append(head);
            sb.append("GreaterThanOrEqualTo(");
            sb.append(ftype);
            sb.append(" value) {\n            return greaterThanOrEqualTo(");
            sb.append(fname);
            sb.append(", value);\n        }\n\n");

            sb.append(head);
            sb.append("LessThan(");
            sb.append(ftype);
            sb.append(" value) {\n            return lessThan(");
            sb.append(fname);
            sb.append(", value);\n        }\n\n");

            sb.append(head);
            sb.append("LessThanOrEqualTo(");
            sb.append(ftype);
            sb.append(" value) {\n            return lessThanOrEqualTo(");
            sb.append(fname);
            sb.append(", value);\n        }\n\n");

            if ("String".equals(ftype)) {
                sb.append(head);
                sb.append("Like(");
                sb.append(ftype);
                sb.append(" value) {\n            return like(");
                sb.append(fname);
                sb.append(", value);\n        }\n\n");

                sb.append(head);
                sb.append("NotLike(");
                sb.append(ftype);
                sb.append(" value) {\n            return notLike(");
                sb.append(fname);
                sb.append(", value);\n        }\n\n");
            }

            sb.append(head);
            sb.append("In(List<");
            sb.append(ftype);
            sb.append("> values) {\n            return in(");
            sb.append(fname);
            sb.append(", values);\n        }\n\n");

            sb.append(head);
            sb.append("NotIn(List<");
            sb.append(ftype);
            sb.append("> values) {\n            return notIn(");
            sb.append(fname);
            sb.append(", values);\n        }\n\n");

            sb.append(head);
            sb.append("Between(");
            sb.append(ftype);
            sb.append(" value1, ");
            sb.append(ftype);
            sb.append(" value2");
            sb.append(") {\n            return between(");
            sb.append(fname);
            sb.append(", value1, value2);\n        }\n\n");

            sb.append(head);
            sb.append("NotBetween(");
            sb.append(ftype);
            sb.append(" value1, ");
            sb.append(ftype);
            sb.append(" value2");
            sb.append(") {\n            return notBetween(");
            sb.append(fname);
            sb.append(", value1, value2);\n        }\n");
        }
        return sb.toString();
    }

    private static String upfirst(String field) {
        char c = field.charAt(0);
        String upname = field;
        if (c > 96 && c < 123) {
            c = (char) (c - 32);
            upname = c + field.substring(1);
        }

        return upname;
    }

    private static String lowfirst(String field) {
        char c = field.charAt(0);
        String upname = field;
        if (c > 64 && c < 91) {
            c = (char) (c + 32);
            upname = c + field.substring(1);
        }

        return upname;
    }

    public static String exampleCreate(String pack, Map<String, String> fields, String beanName) throws ClassNotFoundException {
        Map<String, String> beanFields = new HashMap<>();
        for (Map.Entry<String, String> entry : fields.entrySet()) {
            beanFields.put(dataToField(entry.getKey(), false), entry.getValue());
        }
        String code = mkPackage(pack, pack.replace("examples", "beans") + "." + beanName);
        code += mkOther(beanName, beanFields);
        code += createFields(beanFields, beanName);
        code += "    }\n}";
        return code;
    }

}